package com.smf.lsc.gateway.config;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.crypto.codec.Base64;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import com.smf.lsc.security.util.PermisUrlCheckUtil;


/**
 * 校验Token
 * Created on 2021/11/19.
 *
 * @author ln
 * @since 1.0
 */
@Component
public class AuthClient {

    private RestTemplate restTemplate = new RestTemplate();

    @Value("${security.oauth2.resource.token-info-uri}")
    private String checkTokenUrl;
    
    @Value("${security.oauth2.client.client-id}")
    private String clientId;
    
    @Value("${security.oauth2.client.client-secret}")
    private String clientSecret;
    
    @Value("${security.oauth2.permis-urls:''}")
    private String permisUrls;

    private String tokenName = "token";
    
    public boolean hasPermissionControl(String url) {
    	return PermisUrlCheckUtil.matches(url, permisUrls); 
    }

    public AuthUser accessable(ServerHttpRequest request) {
    	AuthUser authUser=new AuthUser();
        try {
        	
        	String token = request.getHeaders().getFirst("Authorization").replace("bearer", "").trim();
        	
        	HttpHeaders headers = new HttpHeaders();
    		headers.set("Authorization", getAuthorizationHeader(clientId, clientSecret));
    		MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();
    		formData.add(tokenName, token);
    		Map<String, Object> map = postForMap(checkTokenUrl, formData, headers);
    		
    		if (CollectionUtils.isEmpty(map)) {
    			throw new RestClientException("check_token returned empty");
    		}

    		if (map.containsKey("error")) {
    			throw new RestClientException("check_token returned error: " + map.get("error"));
    		}

    		// gh-838
    		if (map.containsKey("active") && !"true".equals(String.valueOf(map.get("active")))) {
    			throw new RestClientException("check_token returned active attribute: " + map.get("active"));
    		}
    		authUser.setFlag(true);
    		authUser.setUserInfo(map.get("userinfo").toString());
    		return authUser;
        } catch (Exception e) {
        	e.printStackTrace();
        	throw e;
        }
    }
    
    private Map<String, Object> postForMap(String path, MultiValueMap<String, String> formData, HttpHeaders headers) {
		if (headers.getContentType() == null) {
			headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
		}
		@SuppressWarnings("rawtypes")
		Map map = restTemplate.exchange(path, HttpMethod.POST,
				new HttpEntity<MultiValueMap<String, String>>(formData, headers), Map.class).getBody();
		@SuppressWarnings("unchecked")
		Map<String, Object> result = map;
		return result;
	}
    
    /**
     * check_token时所需凭证生成
     * @param clientId
     * @param clientSecret
     * @return
     */
    private String getAuthorizationHeader(String clientId, String clientSecret) {

		if(clientId == null || clientSecret == null) {
			throw new IllegalStateException("Null Client ID or Client Secret detected. Endpoint that requires authentication will reject request with 401 error.");
		}

		String creds = String.format("%s:%s", clientId, clientSecret);
		try {
			return "Basic " + new String(Base64.encode(creds.getBytes("UTF-8")));
		}
		catch (UnsupportedEncodingException e) {
			throw new IllegalStateException("Could not convert String");
		}
	}
}